package org.darcy.service.impl;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.darcy.config.shiro.EhcacheService;
import org.darcy.dao.BizCommentMapper;
import org.darcy.entity.BizComment;
import org.darcy.entity.SysConfig;
import org.darcy.entity.SysUser;
import org.darcy.framework.blog.business.enums.CommentStatusEnum;
import org.darcy.framework.blog.business.enums.ConfigKeyEnum;
import org.darcy.framework.blog.business.enums.TemplateKeyEnum;
import org.darcy.framework.blog.exception.BlogCommentException;
import org.darcy.framework.blog.holder.RequestHolder;
import org.darcy.framework.generic.GenericServiceImpl;
import org.darcy.framework.util.HtmlUtil;
import org.darcy.framework.util.IpUtil;
import org.darcy.framework.util.RegexUtils;
import org.darcy.framework.util.RequestUtil;
import org.darcy.framework.util.RestClientUtil;
import org.darcy.framework.util.SessionUtil;
import org.darcy.framework.util.UrlBuildUtil;
import org.darcy.framework.util.XssKillerUtil;
import org.darcy.model.BizCommentModel;
import org.darcy.service.BizCommentService;
import org.darcy.service.MailService;
import org.darcy.service.SysConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

import eu.bitwalker.useragentutils.Browser;
import eu.bitwalker.useragentutils.OperatingSystem;
import eu.bitwalker.useragentutils.UserAgent;
import eu.bitwalker.useragentutils.Version;
import lombok.extern.slf4j.Slf4j;

/**
 * 评论
 */
@Slf4j
@Service
public class BizCommentServiceImpl extends GenericServiceImpl<BizComment> implements BizCommentService {

	@Autowired
	private BizCommentMapper bizCommentMapper;

	@Autowired
	private MailService mailService;

	@Autowired
	private SysConfigService configService;
	
	@Autowired
	private EhcacheService EhcacheService;
	

	@Override
	public IPage<BizCommentModel> query(BizCommentModel param) {
		Page p = new Page(param.getP(), param.getPageSize());
		IPage<BizCommentModel> page = bizCommentMapper.query(p, param);
		return page;
	}

	/**
	 * @param vo
	 * @return
	 */
	@Override
	public Map<String, Object> list(BizCommentModel vo) {
		IPage<BizCommentModel> pageInfo = query(vo);
		Map<String, Object> map = new HashMap<>();
		if (pageInfo != null) {
			map.put("commentList", pageInfo.getRecords());
			map.put("total", pageInfo.getTotal());
			map.put("hasNextPage", pageInfo.getCurrent() < pageInfo.getPages());
			map.put("nextPage", pageInfo.getCurrent() + 1);
		}
		return map;
	}

	/**
	 * admin发表评论
	 *
	 * @param comment
	 * @return
	 */
	@Override
	public void commentForAdmin(BizComment comment) throws BlogCommentException {
		Map config = configService.getConfigs();
		SysUser user = SessionUtil.getUser();
		comment.setQq(user.getQq());
		comment.setEmail(user.getEmail());
		comment.setNickname(user.getNickname());
		comment.setAvatar(user.getAvatar());
		comment.setUserId(user.getId());
		comment.setStatus(CommentStatusEnum.APPROVED.toString());
		comment.setPid(comment.getId());
		comment.setId(null);
		this.comment(comment);
	}

	/**
	 * 发表评论
	 *
	 * @param comment
	 * @return
	 */
	@Override
	public BizComment comment(BizComment comment) throws BlogCommentException {
		SysConfig sysConfig = configService.getByKey(ConfigKeyEnum.ANONYMOUS.getKey());
		boolean anonymous = true;
		if (null != sysConfig) {
			anonymous = "1".equals(sysConfig.getConfigValue());
		}

		// 非匿名且未登录
		if (!anonymous && !SessionUtil.isLogin()) {
			throw new BlogCommentException("站长已关闭匿名评论，请先登录！");
		}

		// 过滤文本内容，防止xss
		this.filterContent(comment);

		// 已登录且非匿名，使用当前登录用户的信息评论
		if (SessionUtil.isLogin()) {
			this.setCurrentLoginUserInfo(comment);
		} else {
			this.setCurrentAnonymousUserInfo(comment);
		}

		// 用户没有头像时， 使用随机默认的头像
		if (StringUtils.isEmpty(comment.getAvatar())) {
			List<String> avatars = configService.getRandomUserAvatar();
			if (!CollectionUtils.isEmpty(avatars)) {
				Collections.shuffle(avatars);
				int randomIndex = new Random().nextInt(avatars.size());
				comment.setAvatar(avatars.get(randomIndex));
			}
		}

		if (StringUtils.isEmpty(comment.getStatus())) {
			comment.setStatus(CommentStatusEnum.VERIFYING.toString());
		}

		// set当前评论者的设备信息
		this.setCurrentDeviceInfo(comment);

		// set当前评论者的位置信息
		this.setCurrentLocation(comment);

		// 保存
		this.insert(comment);

		// 发送邮件通知
		this.sendEmail(comment);
		return comment;
	}

	/**
	 * 过滤评论内容
	 *
	 * @param comment
	 */
	private void filterContent(BizComment comment) {
		String content = comment.getContent();
		if (StringUtils.isEmpty(content) || "\n".equals(content)) {
			throw new BlogCommentException("说点什么吧");
		}
		String url = comment.getUrl();
		String avatar = comment.getAvatar();
		if ((!StringUtils.isEmpty(avatar) && !RegexUtils.isUrl(avatar))
				|| (!StringUtils.isEmpty(url) && !RegexUtils.isUrl(url))) {
			throw new BlogCommentException("链接地址不正确");
		}
		// 过滤非法属性和无用的空标签
		if (!XssKillerUtil.isValid(content) || !XssKillerUtil.isValid(comment.getAvatar())
				|| !XssKillerUtil.isValid(comment.getUrl()) || !XssKillerUtil.isValid(comment.getNickname())
				|| !XssKillerUtil.isValid(comment.getQq()) || !XssKillerUtil.isValid(comment.getEmail())) {
			throw new BlogCommentException("请不要使用特殊标签");
		}
		content = XssKillerUtil.clean(content.trim()).replaceAll("(<p><br></p>)|(<p></p>)", "");
		if (StringUtils.isEmpty(content) || "\n".equals(content)) {
			throw new BlogCommentException("说点什么吧");
		}
		comment.setContent(content);
	}

	/**
	 * 保存当前匿名用户的信息
	 *
	 * @param comment
	 */
	private void setCurrentAnonymousUserInfo(BizComment comment) {
		if (StringUtils.isEmpty(comment.getNickname())) {
			throw new BlogCommentException("必须输入昵称");
		}
		comment.setNickname(HtmlUtil.html2Text(comment.getNickname()));
		comment.setQq(HtmlUtil.html2Text(comment.getQq()));
		comment.setAvatar(HtmlUtil.html2Text(comment.getAvatar()));
		comment.setEmail(HtmlUtil.html2Text(comment.getEmail()));
		comment.setUrl(HtmlUtil.html2Text(comment.getUrl()));
	}

	/**
	 * 保存当前登录用户的信息
	 *
	 * @param comment
	 */
	private void setCurrentLoginUserInfo(BizComment comment) {
		SysUser loginUser = SessionUtil.getUser();
		comment.setNickname(HtmlUtil.html2Text(loginUser.getNickname()));
		comment.setQq(HtmlUtil.html2Text(loginUser.getQq()));
		comment.setAvatar(HtmlUtil.html2Text(loginUser.getAvatar()));
		comment.setEmail(HtmlUtil.html2Text(loginUser.getEmail()));
		comment.setUrl(HtmlUtil.html2Text(loginUser.getBlog()));
		comment.setUserId(loginUser.getId());
	}

	/**
	 * 保存当前评论时的设备信息
	 *
	 * @param comment
	 */
	private void setCurrentDeviceInfo(BizComment comment) {
		String ua = RequestUtil.getUa();
		UserAgent agent = UserAgent.parseUserAgentString(ua);
		Browser browser = agent.getBrowser();
		String browserInfo = browser.getName();
		Version version = agent.getBrowserVersion();
		if (version != null) {
			browserInfo += " " + version.getVersion();
		}
		comment.setBrowser(browserInfo);
		OperatingSystem os = agent.getOperatingSystem();
		comment.setOs(os.getName());
		comment.setIp(RequestUtil.getIp());
	}

	/**
	 * 保存当前评论时的位置信息
	 *
	 * @param comment
	 */
	private void setCurrentLocation(BizComment comment) {
		Map config = configService.getConfigs();
		try {
			String locationJson = RestClientUtil.get(UrlBuildUtil.getLocationByIp(comment.getIp(),
					(String) config.get(ConfigKeyEnum.BAIDU_API_AK.getKey())));
			JSONObject localtionContent = JSONObject.parseObject(locationJson).getJSONObject("content");
			if (localtionContent.containsKey("point")) {
				JSONObject point = localtionContent.getJSONObject("point");
				comment.setLat(point.getString("y"));
				comment.setLng(point.getString("x"));
			}

			if (localtionContent.containsKey("address_detail")) {
				JSONObject addressDetail = localtionContent.getJSONObject("address_detail");
				String city = addressDetail.getString("city");
				String district = addressDetail.getString("district");
				String street = addressDetail.getString("street");
				String address = addressDetail.getString("province") + (StringUtils.isEmpty(city) ? "" : city)
						+ (StringUtils.isEmpty(district) ? "" : district) + (StringUtils.isEmpty(street) ? "" : street);
				comment.setAddress(address);
			}
		} catch (Exception e) {
			log.error("获取地址失败", e);
		}
		if (StringUtils.isEmpty(comment.getAddress())) {
			comment.setAddress("未知");
		}
	}

	private void sendEmail(BizComment comment) {
		// 发送邮件通知，此处如发生异常不应阻塞当前的业务流程
		// 可以进行日志记录等操作
		try {
			if (null != comment.getPid() && 0 != comment.getPid()) {
				// 给被评论的用户发送通知
				BizComment commentDB = this.getById(comment.getPid());
				mailService.send(commentDB, TemplateKeyEnum.TM_COMMENT_REPLY, false);
			} else {
				mailService.sendToAdmin(comment);
			}
		} catch (Exception e) {
			log.error("发送评论通知邮件时发生异常", e);
		}
	}

	/**
	 * 查询近期评论
	 *
	 * @param pageSize
	 * @return
	 */
	@Override
	public List<BizCommentModel> listRecentComment(int pageSize) {
		BizCommentModel vo = new BizCommentModel();
		vo.setPageSize(pageSize);
		vo.setStatus(CommentStatusEnum.APPROVED.toString());
		IPage<BizCommentModel> pageInfo = query(vo);
		return pageInfo.getRecords();
	}

	/**
	 * 查询未审核的评论
	 *
	 * @param pageSize
	 * @return
	 */
	@Override
	public List<BizCommentModel> listVerifying(int pageSize) {
		BizCommentModel vo = new BizCommentModel();
		vo.setPageSize(pageSize);
		vo.setStatus(CommentStatusEnum.VERIFYING.toString());
		IPage<BizCommentModel> pageInfo = query(vo);
		return pageInfo.getRecords();
	}

	/**
	 * 点赞
	 *
	 * @param id
	 */
	@Override
	public void doSupport(Long id) {
		String key = IpUtil.getRealIp(RequestHolder.getRequest()) + "_doSupport_" + id;
		if (EhcacheService.hasKey(key)) {
			throw new BlogCommentException("一个小时只能点一次赞哈~");
		}
		bizCommentMapper.doSupport(id);
		EhcacheService.set(key, id.toString());
	}

	/**
	 * 点踩
	 *
	 * @param id
	 */
	@Override
	public void doOppose(Long id) {
		String key = IpUtil.getRealIp(RequestHolder.getRequest()) + "_doOppose_" + id;
		if (EhcacheService.hasKey(key)) {
			throw new BlogCommentException("一个小时只能踩一次哈~又没什么深仇大恨");
		}
		bizCommentMapper.doOppose(id);
		EhcacheService.set(key, id.toString());
	}

}
